/**
 * @class UI-基础件-访问栏
 * @author DuskyHuang 圣子
 * @description 主要行星、卫星、恒星、黑洞等的快捷访问菜单，根据当前的视野自动切换。
*/

import { _decorator, Node, Toggle, Sprite, Vec3, Layout, Button, tween, UITransform, Widget, EventTouch } from 'cc';
import { ItemList, Mediator, TweenSwitcher, utils } from 'cfw';
import { FIXED_ICONS, Res } from 'const';
import { View, UI } from 'notifications';
import { cosmos, CosmosProxy, planetary, solar, visit } from 'proxies';
import { IIconData } from '../../celestial';
import { CelestialIcon } from '../items/celestial-icon';
const { group } = utils;
const { ccclass } = _decorator;

@ccclass('cocosmos.visit-menu')
export class VisitMenu extends Mediator {

    @group("Scope", "", Node) public scopeGroup: Node = null;
    @group('Scope', "", Button) public visitBtn: Button = null;

    @group("Solar", "", Node) public solarGroup: Node = null;
    @group('Solar', "", CelestialIcon) public sunIcon: CelestialIcon = null;
    @group('Solar', "", CelestialIcon) public planetIcon: CelestialIcon = null;
    @group('Solar', "", Toggle) public planetExpander: Toggle = null;
    @group('Solar', "", Toggle) public dwarfExpander: Toggle = null;
    @group('Solar', "", Toggle) public satelliteExpander: Toggle = null;
    @group('Solar', "", Node) public planetList: Node = null;
    @group('Solar', "", Node) public dwarfList: Node = null;
    @group('Solar', "", Node) public satelliteList: Node = null;

    @group("Stellar", "", Node) public stellarGroup: Node = null;
    @group('Stellar', "", CelestialIcon) public sunIconS: CelestialIcon = null;
    @group('Stellar', "", Toggle) public star10Expander: Toggle = null;
    @group('Stellar', "", Toggle) public star20Expander: Toggle = null;
    @group('Stellar', "", Toggle) public star30Expander: Toggle = null;
    @group('Stellar', "", Toggle) public blackhole07Expander: Toggle = null;
    // @group('Stellar', "", Toggle) public blackhole14Expander: Toggle = null;
    @group('Stellar', "", Node) public star10List: Node = null;
    @group('Stellar', "", Node) public star20List: Node = null;
    @group('Stellar', "", Node) public star30List: Node = null;
    @group('Stellar', "", Node) public blackhole07List: Node = null;
    // @group('Stellar', "", Node) public blackhole14List: Node = null;

    protected onLoad(): void {
        this._init();
        this._listen();
    }

    protected start(): void {
        this.scopeGroup.active = this.solarGroup.active = this.stellarGroup.active = false;
    }

    protected update(): void {
        const { pow10, underSolar, upSolar, underStellar } = cosmos,
            { inSolar, inPlanetary, inSatellite, inStellar, inStar } = visit;
        this._scopes.forEach(btn => {
            const name = btn.node.name as scalar;
            const [head, tail] = VisibleRanges[name];
            btn.node.active = head <= pow10 && pow10 <= tail;
            if (name === 'Planetary') btn.node.active &&= (inPlanetary && planetary.unbelt || inSatellite);
            if (inStellar && ['Planetary', 'Solar', 'Helio'].contains(name)) {
                btn.node.active = false;
            }
        });
        this.sunIcon.node.active = !inSolar || pow10 > -4.5;
        this.sunIconS.node.active = inStar;
        this.planetIcon.node.active = inSatellite;
        const showSolar = this._validate && !inStellar && underSolar;
        const showStellar = this._validate && underStellar && (inStellar || !upSolar);
        this.solarGroup.active = showSolar;
        this.stellarGroup.active = showStellar;
    }

    private _init(): void {
        this._scopes = this.scopeGroup.getComponentsInChildren(Button).slice(1);
        this._expanders = [
            this.planetExpander, this.dwarfExpander, this.satelliteExpander,
            this.star10Expander, this.star20Expander, this.star30Expander,
            this.blackhole07Expander
        ];
        this._lists = [
            this.planetList, this.dwarfList, this.satelliteList,
            this.star10List, this.star20List, this.star30List,
            this.blackhole07List, 
            // this.blackhole14List,
        ];
        this.solarGroup.children.forEach(child => records.set(child, { last: false, from: new Vec3, to: new Vec3 }));
        this.stellarGroup.children.forEach(child => records.set(child, { last: false, from: new Vec3, to: new Vec3 }));
        this._twsScope = this.scopeGroup.getComponent(TweenSwitcher);
        this._twsSolar = this.solarGroup.getComponent(TweenSwitcher);
        this._twsStellar = this.stellarGroup.getComponent(TweenSwitcher);
        this._satIcons = this.satelliteList.getComponent(ItemList<CelestialIcon>);
        this._loadFixedIcons();
    }

    private _loadFixedIcons(): void {
        this.sunIcon.onDataUpdated(FIXED_ICONS.SUN as IIconData);
        this.sunIconS.onDataUpdated(FIXED_ICONS.SUN as IIconData);
        this.planetList.getComponent(ItemList).updateList(FIXED_ICONS.PLANETS);
        this.dwarfList.getComponent(ItemList).updateList(FIXED_ICONS.DWARFS);
        this.star10List.getComponent(ItemList).updateList(FIXED_ICONS.STARS10);
        this.star20List.getComponent(ItemList).updateList(FIXED_ICONS.STARS20);
        this.star30List.getComponent(ItemList).updateList(FIXED_ICONS.STARS30);
        this.blackhole07List.getComponent(ItemList).updateList(FIXED_ICONS.BH07);
        // this.blackhole14List.getComponent(ItemList).updateList(FIXED_ICONS.BH14);
    }

    private _listen(): void {
        this.on(Transform.ZOOM_START, this.onVisitStart);
        this.on(Transform.VISIT_OVER, this.onVisitOver);
    }


    protected clickSearch(): void {
        this.notify(UI.Panels.OPEN, Res.Prefabs.VISIT_PANEL);
    }

    protected clickScope(event: EventTouch): void {
        const name = event.target.name;
        const base = Bases[name as scalar];
        cosmos.zoomToBy(base, TRANS_SPEED_SCOPE, Math.randomBool());
        this.notify(View.Lens.ANGLE_ADDED, 0, TRANS_HIGHT);
        this._popHeadingInfo(base, name);
    }

    protected onVisitStart(): void {
        this._twsScope.hide(true);
        this._twsSolar.hide();
        this._twsStellar.hide();
    }

    private _validate: boolean = false;
    protected onVisitOver(): void {
        this._validate = true;
        this._resetUIByScope();
        this._twsScope.show(true);
        this._twsSolar.show();
        this._twsStellar.show();
    }

    protected expandList(expander: Toggle, data: string): void {
        if (data === 'hide-left') {
            this.scopeGroup.active = !expander.isChecked;
        }
        expander.getComponent(Sprite).enabled = !expander.isChecked;
        const group = expander.node.parent;
        const list = group.getChildByName(expander.node.name.replace('Expander:expand', 'List'));
        const transf = group.getComponent(UITransform);
        const layout = group.getComponent(Layout);
        const widget = group.getComponent(Widget);
        const former = transf.width;
        this._record(true, group);
        if (expander.isChecked) {
            this._foldOthers(expander, list);
            list.active = true;
            list.getComponent(Layout).updateLayout();
        } else {
            list.active = false;
        }
        layout.updateLayout();
        widget.updateAlignment();
        this._record(false, group);
        const latest = transf.width;
        changed.x = (latest - former) / 2;
        group.children.forEach(child => {
            const record = records.get(child);
            if (child.active) {
                if (!record.last) {
                    child.getComponent(TweenSwitcher)?.resetOrigin().show(true);
                } else {
                    const { from, to } = record;
                    tween(child).set({ position: from.add(changed) })
                        .to(DURATION, { position: to }, { easing: 'sineOut' }).start();
                }
            }
        });
    }

    private _popHeadingInfo(dest: pow10, name: string): void {
        if (Math.abs(dest - cosmos.pow10) > POP_INFO_TOLERANCE) {
            this.notify(UI.Popups.INFO_LINE, `scopes@goto-${name}`);
        }
    }

    protected clickSolar(): void {
        const dest = solar.systemScope;
        if (visit.inSolar) {
            cosmos.zoomToBy(dest, TRANS_SPEED_SCOPE, Math.randomBool());
            this.notify(View.Lens.ANGLE_CHANGE, 0, 20);
        } else {
            this.notify(UI.VISIT, 'Sun', 'star', dest);
            this.notify(UI.IGNORE_TARGET_ONCE);
        }
        this._popHeadingInfo(dest, 'solar');
    }

    protected clickPlanetary(): void {
        const { centerName, center: { cat, rotator }, systemScope } = planetary;
        const { inPlanetary, inSatellite } = visit;
        if (inPlanetary) {
            cosmos.zoomToBy(systemScope, 0.5);
            const { x, y, z } = Vec3.normalize(v3a, rotator.up);
            const az = Math.parseDegree(Math.atan2(x, z));
            const ha = Math.parseDegree(Math.atan(y));
            this.notify(View.Lens.ANGLE_CHANGE, az, ha);
        } else if (inSatellite) {
            this.notify(UI.VISIT, centerName, cat as into, systemScope);
        }
        this._popHeadingInfo(systemScope, 'planetary');
    }

    protected clickSunIcon(): void {
        if (visit.inSolar) {
            cosmos.zoomToBy(visit.center.suggest, 0.5);
        } else {
            this.notify(UI.VISIT, 'Sun', 'star');
        }
    }

    protected clickPlanetIcon(): void {
        const { inPlanetary, inSatellite } = visit;
        const { centerName, center: { cat, suggest } } = planetary;
        if (inPlanetary) {
            cosmos.zoomToBy(suggest, 1);
        } else if (inSatellite) {
            this.notify(UI.VISIT, centerName, cat as into);
        }
    }

    private _resetUIByScope(): void {
        const { inSolar, inPlanetary, inSatellite } = visit;
        this.visitBtn.node.active = inSolar;
        this.satelliteExpander.node.active = false;
        if (inPlanetary || inSatellite) {
            const { satellites, center: { cat }, unbelt, centerName: name } = planetary;
            if (satellites.length > 0) {
                this.satelliteExpander.node.active = true;
            }
            if (unbelt) {
                this.planetIcon.onDataUpdated({ into: cat as into, name, label: name });
            }
            this._reloadSatellitesList();
        }
        this.planetExpander.node.active = this.dwarfExpander.node.active = inSolar || inPlanetary;
        this._lists.forEach(list => list.active = false);
        this._foldOthers(null);
    }

    private _reloadSatellitesList(): void {
        const { satellites } = planetary;
        this._satIcons.updateList(satellites.map(name => <IIconData>{ into: 'satellite', name }));
        this.satelliteList.getComponent(Layout).updateLayout();
    }

    private _record(from: boolean, group: Node): void {
        group.children.forEach(child => {
            const item = records.get(child);
            if (from) {
                item.from.set(child.position);
                item.last = child.active;
            } else item.to.set(child.position);
        });
    }

    private _foldOthers(expander: Toggle, list?: Node): void {
        this._expanders.forEach(item => {
            if (item !== expander && item.isChecked) {
                item.getComponent(Sprite).enabled = true;
                item.setIsCheckedWithoutNotify(false);
            }
        });
        if (list) this._lists.forEach(item => { if (item !== list) item.active = false });
    }

    private _scopes: Button[];
    private _expanders: Toggle[] = [];
    private _satIcons: ItemList<CelestialIcon>;
    private _twsScope: TweenSwitcher;
    private _twsSolar: TweenSwitcher;
    private _twsStellar: TweenSwitcher;
    private _lists: Node[] = [];
}

const { Transform } = View;
const { Bases, TRANS_SPEED_SCOPE, TRANS_HIGHT } = CosmosProxy;
const DURATION: num.px = 0.3;
const changed = new Vec3;
const records: Map<Node, { last: boolean, from: Vec3, to: Vec3 }> = new Map();
const v3a = new Vec3;
const POP_INFO_TOLERANCE: pow10 = 0.5;

declare type range = vec2<pow10>;

const VisibleRanges: Partial<Record<scalar, range>> = {
    Planetary: [-99, -3],
    Solar: [-99, 0],
    Helio: [-4, 3],
    Stellar: [-1, 5.3],
    MilkyWay: [1.8, 7.8],
    LocalGG: [3.8, 9.8],
    VirgoSC: [4.9, 10.9],
    Laniakea: [5.6, 11.3],
    Observable: [8, 99],
}